﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;

using ERPStore.Validators;
using ERPStore.Extensions;
using ERPStore.Web.Routing;

using CloudSoft.Extensions;

namespace ERPStore.Offers.Services
{
	public class AdminOffersService
	{
		public AdminOffersService(CloudSoft.Repositories.IRepository<Repositories.OffersDbContext> repository
			, ERPStore.Services.IValidatorService validatorService
			, ERPStore.Services.ICacheService cacheService)
		{
			this.OfferRepository = repository;
			this.ValidatorService = validatorService;
			this.CacheService = cacheService;
		}

		internal CloudSoft.Repositories.IRepository<Repositories.OffersDbContext> OfferRepository { get; private set; }
		internal ERPStore.Services.IValidatorService ValidatorService { get; private set; }
		internal ERPStore.Services.ICacheService CacheService { get; private set; }

		public IQueryable<Models.OfferEx> GetAllOffers()
		{
			var datalist = OfferRepository.Query<Repositories.Datas.OfferData>();
			var offerList = AutoMapper.Mapper.Map<List<Models.OfferEx>>(datalist);

			var result = new List<Models.OfferEx>();
			foreach (var offer in offerList)
			{
				var offerId = offer.Id;
				offer.ProductList = GetOfferedProductListBySelectionId(offerId);
				result.Add(offer);
			}

			return result.AsQueryable();
		}

		public List<Models.OfferEx> GetCurrentOffers()
		{
			string key = "AllOffers";

			var list = CacheService[key] as List<Models.OfferEx>;
			if (list != null)
			{
				return list;
			}

			var result = new List<Models.OfferEx>();
			var offerList = GetAllOffers();
			// Elimination des offre périmées
			foreach (var offer in offerList)
			{
				if (offer.StartDate.HasValue
					&& offer.StartDate > DateTime.Today)
				{
					continue;
				}

				if (offer.EndDate.HasValue
					&& offer.EndDate < DateTime.Today)
				{
					continue;
				}

				var offerId = offer.Id;
				offer.ProductList = GetOfferedProductListBySelectionId(offerId);

				result.Add(offer);
			}

			// Tous les jours à 5h du matin
			CacheService.Add(key, result, new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 5, 0, 0).AddDays(1));

			return result;

		}

		public Models.OfferEx GetOfferById(int offerId)
		{
			var offer = GetAllOffers().SingleOrDefault(i => i.Id == offerId);
			return offer;
		}

		public ERPStore.Models.Offer GetOfferByProduct(ERPStore.Models.Product product)
		{
			return GetCurrentOffers().FirstOrDefault(i => i.ProductList.Any(p => p.ProductId == product.Id));
		}

		public Models.OfferEx CreateOffer()
		{
			var result = new Models.OfferEx();
			return result;
		}

		public ERPStore.Models.Offer GetOfferByName(string offerName)
		{
			var offer = GetAllOffers().SingleOrDefault(i => i.Name.Equals(offerName, StringComparison.InvariantCultureIgnoreCase));
			return offer;
		}

		public List<ERPStore.Models.BrokenRule> ValidateOffer(Models.OfferEx offer)
		{
			var result = ValidatorService.Validate(offer);
			return result;
		}

		public List<ERPStore.Models.BrokenRule> ValidateOfferedProduct(Models.OfferedProduct offeredProduct)
		{
			var result = ValidatorService.Validate(offeredProduct);
			return result;
		}

		public void SaveOffer(Models.OfferEx offer)
		{
			var rules = ValidateOffer(offer);
			if (rules.IsNotNullOrEmpty())
			{
				throw new ERPStore.Models.ValidationException(rules);
			}

			bool isNew = false;
			Repositories.Datas.OfferData data = null;
			if (offer.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.OfferData>(offer);
				OfferRepository.Insert<Repositories.Datas.OfferData>(data);
				isNew = true;
			}
			else
			{
				data = OfferRepository.Get<Repositories.Datas.OfferData>(i => i.Id == offer.Id);
				data = AutoMapper.Mapper.Map<Models.OfferEx, Repositories.Datas.OfferData>(offer, data);
				OfferRepository.Update<Repositories.Datas.OfferData>(data);
			}

			offer.Id = data.Id;

			if (isNew)
			{
				var namespaces = new string[] { "ERPStore.Offers.Controllers" };

				var route = RouteTable.Routes.GetByName("OfferLink");
				var index = RouteTable.Routes.IndexOf(route);
				string routeName = string.Format("OfferLink.{0}", offer.Id);

				var offerRoute = RouteTable.Routes.GetByName(routeName);

				if (offerRoute == null)
				{
					offerRoute = RouteTable.Routes.MapERPStoreRoute(
								routeName
								, offer.Link
								, new { controller = "Offers", action = "Offer", offerLink = offer.Link }
								, namespaces
							);

					// RouteTable.Routes.InsertRoute(index - 1, routeName, offerRoute);
				}
			}

			// Shot the cache
			CacheService.Remove("AllOffers");
		}

		public void SaveOfferedProduct(Models.OfferedProduct offeredProduct)
		{
			var rules = ValidateOfferedProduct(offeredProduct);
			if (rules.IsNotNullOrEmpty())
			{
				throw new ERPStore.Models.ValidationException(rules);
			}


			Repositories.Datas.OfferedProductData data = null;
			if (offeredProduct.Id == 0)
			{
				data = AutoMapper.Mapper.Map<Repositories.Datas.OfferedProductData>(offeredProduct);
				OfferRepository.Insert(data);
			}
			else
			{
				data = OfferRepository.Get<Repositories.Datas.OfferedProductData>(i => i.Id == offeredProduct.Id);
				AutoMapper.Mapper.Map<Models.OfferedProduct, Repositories.Datas.OfferedProductData>(offeredProduct, data);
				OfferRepository.Update(data);
			}

			offeredProduct.Id = data.Id;
		}

		public Models.OfferedProduct CreateOfferedProduct()
		{
			var result = new Models.OfferedProduct();
			return result;
		}

		internal void DeleteOfferedProduct(Models.OfferedProduct offeredProduct)
		{
			var data = OfferRepository.Query<Repositories.Datas.OfferedProductData>(i => i.Id == offeredProduct.Id).Single();
			OfferRepository.Delete(data);

			var offers = GetAllOffers().Where(i => i.Id == offeredProduct.OfferId);
			foreach (var offer in offers)
			{
				offer.ProductList = null;
				var offerId = offer.Id;
				offer.ProductList = GetOfferedProductListBySelectionId(offerId);
			}
		}

		private List<Models.OfferedProduct> GetOfferedProductListBySelectionId(int offerId)
		{
			var list = GetOfferedProductListByOfferId(offerId);
			return list.ToList();
		}

		public Models.OfferedProduct GetOfferedProductById(int id)
		{
			var data = OfferRepository.Get<Repositories.Datas.OfferedProductData>(i => i.Id == id);
			return AutoMapper.Mapper.Map<Models.OfferedProduct>(data);
		}

		public IEnumerable<Models.OfferedProduct> GetOfferedProductListByOfferId(int offerId)
		{
			var list = OfferRepository.Query<Repositories.Datas.OfferedProductData>(i => i.OfferId == offerId).ToList();
			var result = AutoMapper.Mapper.Map<IEnumerable<Models.OfferedProduct>>(list);
			return result.OrderBy(i => i.OrderIndex.GetValueOrDefault(i.Id));
		}

		internal void DeleteOffer(Models.OfferEx offer)
		{
			if (!offer.ProductList.IsNullOrEmpty())
			{
				foreach (Models.OfferedProduct item in offer.ProductList)
				{
					var data = OfferRepository.Get<Repositories.Datas.OfferedProductData>(i => i.Id == item.Id);
					if (data != null)
					{
						OfferRepository.Delete(data);
					}
				}
			}

			var offerData = OfferRepository.Get<Repositories.Datas.OfferData>(i => i.Id == offer.Id);
			var deletedCount = OfferRepository.Delete(offerData);

			string routeName = string.Format("OfferLink.{0}", offer.Id);

			var offerRoute = RouteTable.Routes.GetByName(routeName);
			RouteTable.Routes.Remove(offerRoute);

			// Shot the cache
			CacheService.Remove("AllOffers");
		}

		public void RefreshAllOffers()
		{
			CacheService.Remove("AllOffers");
			var offerList = GetAllOffers();
			foreach (var offer in offerList)
			{
				var cacheKey = string.Format("Offer:ProductList:{0}", offer.Id);
				CacheService.Remove(cacheKey);
			}
		}

	}
}
